class Compile {
  constructor(vm) {
    this.el = vm.$el
    this.vm = vm
    this.compile(this.el)
  }
  compile(el) {
    // 深度遍历页面模板节点，解析每个节点指令，表达式等操作进行dom操作
    let childNodes = el.childNodes
    Array.from(childNodes).forEach(node => {
      if(this.isTextNode(node)) {
        this.compileText(node)
      } else if (this.isElementNode(node)) {
        this.compileElement(node)
      }
      if(node.childNodes && node.childNodes.length) {
        this.compile(node)
      }
    })
    // 观察 指令，表达式绑定的变量进行dom操作
  }
  compileElement(node) {
    // 编译元素节点指令属性节点
    // 编译元素属性节点
    Array.from(node.attributes).forEach(attrNode => {
      let attrNodeName = attrNode.name
      if(this.isDirective(attrNodeName)) {
        attrNodeName = attrNodeName.substr(2) // 指令名称
        let key = attrNode.value // 指令绑定的key
        this.update(node, key, attrNodeName)
      }
    })
  }
  // 解析节点指令入口
  update(node,key, name) {
    let updateFn
    if(name && name.startsWith('on')) {
      updateFn = this[`onUpdate`]
    } else {
      updateFn = this[`${name}Update`]
    }
    updateFn && updateFn.call(this, node, key, name) 

  }
  // 解析元素节点html指令
  htmlUpdate(node, key) {
    node.innerHTML = this.vm[key]
    new Watcher(this.vm, key, (newValue) => {
      node.innerHTML = newValue // 
    })
  }
  // 解析元素节点on事件指令
  onUpdate(node, key, name) {
    let eventName = name.substr(3)
    if (this['vm'][key] && typeof this['vm'][key] === 'function') {
      node.addEventListener(eventName, (e) => {
        this['vm'][key](e)
      })
    }
  }
  // 解析文本节点插值表达式
  compileText(node) {
    let reg = /\{\{(.+)\}\}/
    let value = node.textContent
    if(reg.test(value)) {
      // 有插值表达式的节点
      let key = RegExp.$1.trim()
      // 将文本节点内容插值表达式替换
      node.textContent = value.replace(reg,this.vm[key])
      // 插值表达式文本节点观察key
      new Watcher(this.vm, key, (newvalue) => {
        node.textContent = value.replace(reg,newvalue)
      })
    }
  }
  isTextNode(node) {
    return node.nodeType === 3
  }
  isElementNode(node) {
    return node.nodeType === 1
  }
  isDirective(attrName) {
        return attrName.startsWith('v-')
    }
}